在接下來的幾天深入研究 Signal API 之前,我想建立一個簡單的 Angular 應用程式來顯示所有 100 個貼文。 該應用程式演示了該框架的基本構建塊:
現在,我將透過定義 TypeScript 類型、服務和幾個元件來完成逐步教學。 請按照第一天的說明在瀏覽器上建立一個新的 Angular 專案。
我們的組件在列表中顯示貼文;因此,使用 Post
類型來進行類型安全和類型檢查是一個很好的做法。在檔案總管中,在 src
資料夾下新增一個名為 post.type.ts
的檔案。
// post.type.ts
export type Post = {
id: number;
userId: number;
title: string;
body: string;
};
服務負責在 Angular 中取得和管理資料。在此範例中,PostService
提供了記憶體中的 Post
物件陣列。元件可以注入此服務來存取貼文並將其顯示在 UI 中。
// post.service.ts
import { Injectable } from '@angular/core';
import { Post } from './post.type';
@Injectable({
providedIn: 'root'
})
export class PostService {
posts: Post[] = [
{
"userId": 1,
"id": 1,
"title": "sunt aut facere repellat provident occaecati excepturi optio reprehenderit",
"body": "quia et suscipit\nsuscipit recusandae consequuntur expedita et cum\nreprehenderit molestiae ut ut quas totam\nnostrum rerum est autem sunt rem eveniet architecto"
},
{
"userId": 1,
"id": 2,
"title": "qui est esse",
"body": "est rerum tempore vitae\nsequi sint nihil reprehenderit dolor beatae ea dolores neque\nfugiat blanditiis voluptate porro vel nihil molestiae ut reiciendis\nqui aperiam non debitis possimus qui neque nisi nulla"
},
... omit the next 98 Post objects ...
]
}
然後,任何組件都可以注入PostService
服務來存取posts
陣列。
Angular採用組件架構,即將一個大組件分解為幾個小組件,以便於維護、可讀性、協作性和良好的性能。
App
|----- PostsComponent
|-----------PostComponent
讓我們建立一個代表單一貼文的PostComponent
。在src
資料夾下,建立一個PostComponent
類別。
// post.component.ts
import { ChangeDetectionStrategy, Component, input, output } from "@angular/core";
import { Post } from "./post.type";
@Component({
selector: 'app-post',
standalone: true,
template: `
<p><span class="text">Id: </span>{{ post().id }}</p>
<p><span class="text">Title: </span>{{ post().title }}</p>
<p><span class="text">Body: </span>{{ post().body }}</p>
<button (click)="deleteClicked.emit(post().id)">Delete</button>
<hr />
`,
styles: `
.text {
font-style: italic;
color: #a4a4a4;
}
button {
margin-bottom: 0.5rem;
}
`,
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class PostComponent {
post = input.required<Post>();
deleteClicked = output<number>();
}
selector: 'app-post',
standalone: true,
組件標籤為 <app-post [post]="post" (deleteClicked)="..." />
,它是一個獨立的組件。
template: `
<p><span class="text">Id: </span>{{ post().id }}</p>
<p><span class="text">Title: </span>{{ post().title }}</p>
<p><span class="text">Body: </span>{{ post().body }}</p>
<button (click)="deleteClicked.emit(post().id)">Delete</button>
<hr />
`,
HTML範本顯示貼文的 ID、標題、內文以及點擊後刪除目前貼文的按鈕。 該按鈕還說明了一個稱為事件發射 (event emitter) 的重要概念,該概念將事件資料發射到包含它的組件。
(click)="deleteClicked.emit(post().id)"
按鈕點擊事件將貼文 ID 傳送到其父組件(即 PostsComponent
)。
styles: `
.text {
font-style: italic;
color: #a4a4a4;
}
button {
margin-bottom: 0.5rem;
}
`
套用於HTML範本的按鈕和span elements的CSS樣式。
export class PostComponent {
post = input.required<Post>();
deleteClicked = output<number>();
}
該組件有一個post
輸入,它是一個Signal輸入(Signal和Signal輸入將在本系列中詳細解釋),而 deleteClicked
是一個自訂事件,它向parent component發出事件資料。
讓我們建立一個由PostComponent
組成的PostsComponent
。
import { ChangeDetectionStrategy, Component, inject } from '@angular/core';
import 'zone.js';
import { PostService } from './post.service';
import { PostComponent } from './post.component';
@Component({
selector: 'app-posts',
standalone: true,
imports: [PostComponent],
template: `
<div>
<p>Number of posts: {{ filteredPosts.length }}</p>
<button (click)="restore()">Restore</button>
</div>
@for (post of filteredPosts; track post.id) {
<app-post [post]="post" (deleteClicked)="delete($event)" />
}
`,
styles: `
div {
display: flex;
align-items: center;
}
div > p {
margin-right: 0.5rem;
}
`,
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class PostsComponent {
postService = inject(PostService);
filteredPosts = [...this.postService.posts];
delete(id: number) {
this.filteredPosts = this.filteredPosts.filter((post) => post.id !== id);
}
restore() {
this.filteredPosts = [...this.postService.posts];
}
}
imports: [PostComponent],
PostsComponent
匯入PostComponent
以在清單中顯示資料。
template: `
<div>
<p>Number of posts: {{ filteredPosts.length }}</p>
<button (click)="restore()">Restore</button>
</div>
@for (post of filteredPosts; track post.id) {
<app-post [post]="post" (deleteClicked)="delete($event)" />
}
`,
HTML範本使用@for
內建控制項來渲染<app-post>
並顯示所有貼文。<app-post>
接收Post
輸入並發出deleteClicked
自訂事件以呼叫delete
方法來修改filteredPosts
陣列。 HTML按鈕元素的clicked
事件執行restore
方法以重設所有貼文。
postService = inject(PostService);
filteredPosts = [...this.postService.posts];
該組件透過依賴注入來注入PostService
。 filteredPosts
變數spread(...) this.postService.posts
以複製Post
陣列。
delete(id: number) {
this.filteredPosts = this.filteredPosts.filter((post) => post.id !== id);
}
restore() {
this.filteredPosts = [...this.postService.posts];
}
delete
方法刪除具有匹配id的貼文,restore
方法會建立filteredPosts
變數的新副本。
import { Component } from '@angular/core';
import { PostsComponent } from './posts.component';
@Component({
selector: 'app-root',
standalone: true,
imports: [PostsComponent],
template: `
<h1>Hello from {{ name }}!</h1>
<h2>Posts List</h2>
<app-posts />
`,
})
export class App {
name = 'IT Home Ironman 2024 day 2';
}
App
組件使用PostsComponent
組件來顯示貼文清單。
如果您對Angular感興趣,Angular團隊提供了可以在Playground或電腦上運行的教學。
鐵人賽的第二天就到此結束。
好棒!就想看這個 3Q
這是用ai寫的嗎
I wrote all my blog posts in English and then used Google Translate to translate the paragraphs.
我所有的部落格文章都是用英語寫的,然後使用谷歌翻譯來翻譯這些段落。
有在FB社團看到了XD
你在國外工作?國外專案有開始用signal了嗎?
請繼續分享~ 感恩!